<?php
// @formatter:off
/**
 * @file ct.lists.losts.php
 * @author Alejandro Dario Simi
 * @date $Date: 2013-08-20 00:23:06 +0000 (Tue, 20 Aug 2013) $
 *
 * $Id: ct.lists.losts.php 102 2013-08-20 00:23:06Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/tags/wcomix-1.0/cron/ct.lists.losts.php $
 */
// @formatter:on

/**
 * @class WCListsLosts
 */
class WCListsLosts extends WCCronTool {
	/**
	 * This variable indicates if check should be retried.
	 *
	 * When solving issues, if one is solved it could mean another issue
	 * appeared. So, checks must be performed until there's not successful
	 * issue solving operation.
	 *
	 * @var boolean
	 */
	protected $_retry = false;
	//
	// Public methods.
	public function run($spacer = "") {
		//
		// Loading global pointers.
		global $wcDefaults;
		global $wcTagsHolder;
		global $wcSpecialTags;
		global $wcPatterns;
		global $wcGroupsHolder;
		//
		// Checking non-group dependent issues.
		echo "{$spacer}Checking possible global issues:\n";
		$this->_retry = true;
		while($this->_retry) {
			$this->_retry = false;

			$this->noUserData("{$spacer}\t");
		}
		if($wcDefaults["read-only"]) {
			echo "{$spacer}".WCColorText::ShellYellow("ReadOnlyMode activated.\n");
		}
		//
		// Checking every group.
		foreach($wcGroupsHolder->groups() as $group) {
			if(is_readable($group->root)) {
				echo "{$spacer}Checking group '{$group->name}' ({$group->id}):\n";

				$this->_retry = true;
				while($this->_retry) {
					$this->_retry = false;

					$this->lostComics($group, "{$spacer}\t");
					$this->lostMembers($group, "{$spacer}\t");
					$this->emptyTags($group, "{$spacer}\t");
					$this->noCollectionComics($group, "{$spacer}\t");
					$this->noCollectionCollections($group, "{$spacer}\t");
					$this->noTrashCan($group, "{$spacer}\t");
					$this->noReadRates($group, "{$spacer}\t");
				}
			}
		}
	}
	public function showHelp($spacer = "") {
		echo "{$spacer}This task performs a series of checks inside the database looking for consistency issues.\n\n";
		echo "{$spacer}Issues:\n";
		echo "{$spacer}\t- User Issues:\n";
		echo "{$spacer}\t\t- Extra information of removed users.\n";
		echo "{$spacer}\t- Comic Issues:\n";
		echo "{$spacer}\t\t- Comic directory id invalid\n";
		echo "{$spacer}\t\t- Comic collection id invalid\n";
		echo "{$spacer}\t- Members Issues:\n";
		echo "{$spacer}\t\t- Members of no tag\n";
		echo "{$spacer}\t\t- Invalid comic members\n";
		echo "{$spacer}\t- Tags Issues:\n";
		echo "{$spacer}\t\t- Tag without members\n";
		echo "{$spacer}\t- Collections Issues:\n";
		echo "{$spacer}\t\t- Collection parent id invalid\n";
		echo "{$spacer}\t- Directory Issues:\n";
		echo "{$spacer}\t\t- Trash can is not added to group\n";
		echo "{$spacer}\t- Rating Issues:\n";
		echo "{$spacer}\t\t- Reads/rates of no comic\n";
		echo "{$spacer}\t\t- Reads/rates of no user\n";

		echo "\n";

		echo "{$spacer}Options:\n";
		echo "{$spacer}\t-c, --solve-comics\n";
		echo "{$spacer}\t\tAutomatically solves any comic issue.\n\n";
		echo "{$spacer}\t-d, --solve-directories\n";
		echo "{$spacer}\t\tAutomatically solves any directory issue.\n\n";
		echo "{$spacer}\t-m, --solve-members\n";
		echo "{$spacer}\t\tAutomatically removes any tag member pointing to a not existing comic.\n\n";
		echo "{$spacer}\t-t, --solve-tags\n";
		echo "{$spacer}\t\tAutomatically removes any tag without members.\n";
		echo "{$spacer}\t\tNote: Special tags won't be affected.\n\n";
		echo "{$spacer}\t-l, --solve-collections\n";
		echo "{$spacer}\t\tAutomatically set collection parents to none if current parent is invalid.\n\n";
		echo "{$spacer}\t-r, --solve-rates\n";
		echo "{$spacer}\t\tAutomatically removes rates of no-user or no-comic.\n\n";
		echo "{$spacer}\t-u, --solve-users\n";
		echo "{$spacer}\t\tAutomatically solves any user issue.\n\n";
	}
	//
	// Protected methods.
	protected function emptyTags(WCGroup &$group, $spacer = "") {
		$solve = $this->solve("SolveTags");

		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  *\n";
			$query .= "from    {$this->_dbprefix}tags\n";
			$query .= "where   tag_id not in (\n";
			$query .= "                select  distinct mem_tag\n";
			$query .= "                from    {$this->_dbprefix}tag_members\n";
			$query .= "                where   mem_group = :group)\n";
			$query .= " and    tag_group = :group\n";

			$stmt = $this->_db->prepare($stmtId, $query);
		}

		if($stmt->execute(array(":group" => $group->id))) {
			global $wcSpecialTags;
			global $wcRuleHooks;

			$specials = array_values($wcSpecialTags);
			$locked = array();
			foreach($wcRuleHooks as $hook) {
				if(isset($hook["lock-tags"])) {
					eval("\$locked = array_merge(\$locked, {$hook["lock-tags"]}(\$group));");
				}
			}
			$locked = array_unique($locked);

			foreach($stmt->fetchAll() as $row) {
				if(!in_array($row["tag_name"], $specials) && !in_array($row["tag_id"], $locked)) {
					echo "{$spacer}Tag '{$row["tag_name"]}' ({$row["tag_id"]}) has no members, therefore it's not longer valid";
					if($solve) {
						global $wcTagsHolder;

						$tag = @$wcTagsHolder->item($row["tag_id"], $group);
						if($tag && $tag->ok() && $tag->remove()) {
							echo ": ".WCColorText::ShellGreen("Solved")."\n";
							$this->_retry = true;
						} else {
							echo ": ".WCColorText::ShellRed("Failed")."\n";
						}
					} else {
						echo ".\n";
					}
				}
			}
		}
	}
	protected function loadOptions() {
		if(!$this->_optionsLoaded) {
			global $wcOptions;

			if($wcOptions->addOption("SolveComics")) {
				$wcOptions->addOptionTrigger("SolveComics", "c");
				$wcOptions->addOptionTrigger("SolveComics", "solve-comics");
			}
			if($wcOptions->addOption("SolveMembers")) {
				$wcOptions->addOptionTrigger("SolveMembers", "m");
				$wcOptions->addOptionTrigger("SolveMembers", "solve-members");
			}
			if($wcOptions->addOption("SolveTags")) {
				$wcOptions->addOptionTrigger("SolveTags", "t");
				$wcOptions->addOptionTrigger("SolveTags", "solve-tags");
			}
			if($wcOptions->addOption("SolveCollections")) {
				$wcOptions->addOptionTrigger("SolveCollections", "l");
				$wcOptions->addOptionTrigger("SolveCollections", "solve-collections");
			}
			if($wcOptions->addOption("SolveDirectories")) {
				$wcOptions->addOptionTrigger("SolveDirectories", "d");
				$wcOptions->addOptionTrigger("SolveDirectories", "solve-directories");
			}
			if($wcOptions->addOption("SolveReadRates")) {
				$wcOptions->addOptionTrigger("SolveReadRates", "r");
				$wcOptions->addOptionTrigger("SolveReadRates", "solve-rates");
			}
			if($wcOptions->addOption("SolveUsers")) {
				$wcOptions->addOptionTrigger("SolveUsers", "u");
				$wcOptions->addOptionTrigger("SolveUsers", "solve-users");
			}

			$wcOptions->check();

			$this->_optionsLoaded = true;
		}
	}
	protected function lostComics(WCGroup &$group, $spacer = "") {
		$solve = $this->solve("SolveComics");

		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  *\n";
			$query .= "from    {$this->_dbprefix}comics\n";
			$query .= "where   cmc_lost      = 'N' \n";
			$query .= " and    cmc_directory not in (\n";
			$query .= "                select  dir_id\n";
			$query .= "                from    {$this->_dbprefix}directories\n";
			$query .= "                where   dir_group = :group\n";
			$query .= "                 and    dir_lost  = 'N')\n";
			$query .= " and    cmc_group = :group\n";

			$stmt = $this->_db->prepare($stmtId, $query);
		}

		if($stmt->execute(array(":group" => $group->id))) {
			global $wcComicsHolder;

			foreach($stmt->fetchAll() as $row) {
				echo "{$spacer}Comic '{$row["cmc_name"]}' ({$row["cmc_id"]}) belongs to no directory";
				if($solve) {
					$comic = $wcComicsHolder->item($row["cmc_id"], $group);
					if($comic) {
						$comic->lost = true;
					}

					if($comic && $comic->lost) {
						echo ": ".WCColorText::ShellGreen("Solved")."\n";
						$this->_retry = true;
					} else {
						echo ": ".WCColorText::ShellRed("Failed")."\n";
					}
				} else {
					echo ".\n";
				}
			}
		}
	}
	protected function lostMembers(WCGroup &$group, $spacer = "") {
		$solve = $this->solve("SolveMembers");

		$stmtS = null;
		if($solve) {
			$stmtIdS = __CLASS__."::".__FUNCTION__."[solve]";
			if($this->_db->has($stmtIdS)) {
				$stmtS = $this->_db->get($stmtIdS);
			} else {
				$query = "delete from {$this->_dbprefix}tag_members\n";
				$query .= "where       mem_group = :group\n";
				$query .= " and        mem_tag   = :tag\n";
				$query .= " and        mem_comic = :comic\n";

				$stmtS = $this->_db->prepare($stmtIdS, $query);
			}
		}

		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__."[nocomic]";
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  distinct *\n";
			$query .= "from    {$this->_dbprefix}tag_members\n";
			$query .= "where   mem_comic not in (\n";
			$query .= "                select  cmc_id\n";
			$query .= "                from    {$this->_dbprefix}comics\n";
			$query .= "                where   cmc_group = :group\n";
			$query .= "                 and    cmc_lost  = 'N')\n";
			$query .= " and    mem_group = :group\n";

			$stmt = $this->_db->prepare($stmtId, $query);
		}

		if($stmt->execute(array(":group" => $group->id))) {
			foreach($stmt->fetchAll() as $row) {
				echo "{$spacer}Member '{$row["mem_comic"]}' of tag '{$row["mem_tag"]}' in group '{$group->name}' is not longer valid";
				if($solve) {
					$params = array(
						":group" => $group->id,
						":tag" => $row["mem_tag"],
						":comic" => $row["mem_comic"]
					);

					if($stmtS->execute($params) && $stmtS->rowCount() > 0) {
						echo ": ".WCColorText::ShellGreen("Solved")."\n";
						$this->_retry = true;

						global $wcTagsHolder;
						$tag = @$wcTagsHolder->item($row["mem_tag"], $group);
						if($tag && $tag->ok()) {
							$tag->count -= $stmtS->rowCount();
						}
					} else {
						echo ": ".WCColorText::ShellRed("Failed")."\n";
					}
				} else {
					echo ".\n";
				}
			}
		}

		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__."[notag]";
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  distinct *\n";
			$query .= "from    {$this->_dbprefix}tag_members\n";
			$query .= "where   mem_tag not in (\n";
			$query .= "                select  tag_id\n";
			$query .= "                from    {$this->_dbprefix}tags\n";
			$query .= "                where   tag_group = :group)\n";
			$query .= " and    mem_group = :group\n";

			$stmt = $this->_db->prepare($stmtId, $query);
		}

		if($stmt->execute(array(":group" => $group->id))) {
			foreach($stmt->fetchAll() as $row) {
				echo "{$spacer}Member tag '{$row["mem_tag"]}' of comic '{$row["mem_comic"]}' in group '{$group->name}' is not longer valid";
				if($solve) {
					$params = array(
						":group" => $group->id,
						":tag" => $row["mem_tag"],
						":comic" => $row["mem_comic"]
					);

					if($stmtS->execute($params) && $stmtS->rowCount() > 0) {
						echo ": ".WCColorText::ShellGreen("Solved")."\n";
						$this->_retry = true;
					} else {
						echo ": ".WCColorText::ShellRed("Failed")."\n";
					}
				} else {
					echo ".\n";
				}
			}
		}
	}
	protected function noCollectionCollections(WCGroup &$group, $spacer = "") {
		$solve = $this->solve("SolveCollections");

		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  distinct *\n";
			$query .= "from    {$this->_dbprefix}collections\n";
			$query .= "where   col_group   =  :group\n";
			$query .= " and    col_parent <> 0\n";
			$query .= " and    col_parent not in (\n";
			$query .= "                select  col_id\n";
			$query .= "                from    {$this->_dbprefix}collections\n";
			$query .= "                where   col_group = :group)\n";

			$stmt = $this->_db->prepare($stmtId, $query);
		}

		if($stmt->execute(array(":group" => $group->id))) {
			foreach($stmt->fetchAll() as $row) {
				echo "{$spacer}solve-collections '{$row["col_name"]}' ({$row["col_id"]}) in group '{$group->name}' belongs to a non-existing collection '{$row["col_parent"]}'";
				if($solve) {
					global $wcCollectionsHolder;

					$collection = $wcCollectionsHolder->item($row["col_id"], $group);
					if($collection && $collection->ok()) {
						$collection->parent = 0;

						echo ": ".WCColorText::ShellGreen("Solved")."\n";
						$this->_retry = true;
					} else {
						echo ": ".WCColorText::ShellRed("Failed")."\n";
					}
				} else {
					echo ".\n";
				}
			}
		}
	}
	protected function noCollectionComics(WCGroup &$group, $spacer = "") {
		$solve = $this->solve("SolveComics");

		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  distinct *\n";
			$query .= "from    {$this->_dbprefix}comics\n";
			$query .= "where   cmc_group      =  :group\n";
			$query .= " and    cmc_lost       =  'N'\n";
			$query .= " and    cmc_collection <> 0\n";
			$query .= " and    cmc_collection not in (\n";
			$query .= "                select  col_id\n";
			$query .= "                from    {$this->_dbprefix}collections\n";
			$query .= "                where   col_group = :group)\n";

			$stmt = $this->_db->prepare($stmtId, $query);
		}

		if($stmt->execute(array(":group" => $group->id))) {
			foreach($stmt->fetchAll() as $row) {
				echo "{$spacer}Comic '{$row["cmc_name"]}' ({$row["cmc_id"]}) in group '{$group->name}' belongs to a non-existing collection '{$row["cmc_collection"]}'";
				if($solve) {
					global $wcComicsHolder;

					$comic = $wcComicsHolder->item($row["cmc_id"], $group);
					if($comic && $comic->ok()) {
						$comic->collection = 0;

						echo ": ".WCColorText::ShellGreen("Solved")."\n";
						$this->_retry = true;
					} else {
						echo ": ".WCColorText::ShellRed("Failed")."\n";
					}
				} else {
					echo ".\n";
				}
			}
		}
	}
	protected function noReadRates(WCGroup &$group, $spacer = "") {
		global $wcDirectoriesHolder;

		$solve = $this->solve("SolveReadRates");

		$stmtSubId = __CLASS__."::".__FUNCTION__.($solve ? "_D" : "");

		$stmtC = null;
		$stmtId = "{$stmtSubId}[comic]";
		if($this->_db->has($stmtId)) {
			$stmtC = $this->_db->get($stmtId);
		} else {
			$query = $solve ? "delete\n" : "select  *\n";
			$query .= "from    {$this->_dbprefix}read_rates\n";
			$query .= "where   crr_group = :group\n";
			$query .= " and    crr_comic not in(\n";
			$query .= "                select  cmc_id\n";
			$query .= "                from    {$this->_dbprefix}comics\n";
			$query .= "                where   cmc_group = :group\n";
			$query .= "                 and    cmc_lost  = 'N')\n";

			$stmtC = $this->_db->prepare($stmtId, $query);
		}

		$stmtU = null;
		$stmtId = "{$stmtSubId}[user]";
		if($this->_db->has($stmtId)) {
			$stmtU = $this->_db->get($stmtId);
		} else {
			$query = $solve ? "delete\n" : "select  *\n";
			$query .= "from    {$this->_dbprefix}read_rates\n";
			$query .= "where   crr_group = :group\n";
			$query .= " and    crr_user not in(\n";
			$query .= "                select  usr_id\n";
			$query .= "                from    {$this->_dbprefix}users)\n";

			$stmtU = $this->_db->prepare($stmtId, $query);
		}

		$params = array(":group" => $group->id);
		$stmtC->execute($params);
		$stmtU->execute($params);

		if($stmtC->rowCount()) {
			echo "{$spacer}Reads/Rates of no comics: ".$stmtC->rowCount();
			echo ": ".WCColorText::ShellGreen($solve ? "Solved" : "Found")."\n";
			if($solve) {
				$this->_retry = true;
			}
		}
		if($stmtU->rowCount()) {
			echo "{$spacer}Reads/Rates of no user: ".$stmtU->rowCount();
			echo ": ".WCColorText::ShellGreen($solve ? "Solved" : "Found")."\n";
			if($solve) {
				$this->_retry = true;
			}
		}
	}
	protected function noTrashCan(WCGroup &$group, $spacer = "") {
		global $wcDirectoriesHolder;

		$solve = $this->solve("SolveDirectories");

		if($group->trash) {
			$trashcan = @$wcDirectoriesHolder->itemByName($group->trash, $group);
			if(!$trashcan) {
				echo "{$spacer}Trashcan '{$group->trash}' of group '{$group->name}' doesn't seem to be present";
				if($solve) {
					$wcDirectoriesHolder->create($group->trash, $group);
					$trashcan = @$wcDirectoriesHolder->item($group->trash, $group);
					if($trashcan) {
						echo ": ".WCColorText::ShellGreen("Solved")."\n";
						$this->_retry = true;
					} else {
						echo ": ".WCColorText::ShellRed("Failed")."\n";
					}
				} else {
					echo ".\n";
				}
			}
		}
	}
	protected function noUserData($spacer = "") {
		global $wcUsersHolder;
		global $wcUserDataHolder;

		$solve = $this->solve("SolveUsers");

		//
		// Extra information of no user.
		// @{
		foreach($wcUserDataHolder->items() as $data) {
			$user = $wcUsersHolder->item($data->id());
			if(!$user) {
				echo "{$spacer}Data for user '{$data->id()}' is no longer valid";
				if($solve) {
					if($data->remove()) {
						echo ": ".WCColorText::ShellGreen("Solved")."\n";
					} else {
						echo ": ".WCColorText::ShellRed("Failed")."\n";
					}
				} else {
					echo ".\n";
				}
			}
		}
		// @}

		//
		// Group permissions of no user.
		// @{
		$stmtGS = null;
		if($solve) {
			$stmtIdGS = __CLASS__."::".__FUNCTION__."[group-solve]";
			if($this->_db->has($stmtIdGS)) {
				$stmtS = $this->_db->get($stmtIdGS);
			} else {
				$query = "delete from     {$this->_dbprefix}group_users\n";
				$query .= "where           rel_gu_user not in (\n";
				$query .= "                        select  usr_id\n";
				$query .= "                        from    {$this->_dbprefix}users)\n";

				$stmtGS = $this->_db->prepare($stmtIdGS, $query);
			}
		}

		$stmtG = null;
		$stmtIdG = __CLASS__."::".__FUNCTION__."[group]";
		if($this->_db->has($stmtIdG)) {
			$stmtG = $this->_db->get($stmtIdG);
		} else {
			$query = "select  *\n";
			$query .= "from    {$this->_dbprefix}group_users\n";
			$query .= "where   rel_gu_user not in (\n";
			$query .= "                select  usr_id\n";
			$query .= "                from    {$this->_dbprefix}users)\n";

			$stmtG = $this->_db->prepare($stmtIdG, $query);
		}

		$stmtG->execute();

		$issues = $stmtG->rowCount();
		if($issues) {
			echo "{$spacer}Group permissions of no user: {$issues}";
			if($solve) {
				$stmtGS->execute();
				if($stmtGS->rowCount() == $issues) {
					echo ": ".WCColorText::ShellGreen("Solved")."\n";
				} else {
					echo ": ".WCColorText::ShellRed("Failed")."\n";
				}
			} else {
				echo ".\n";
			}
		}
		// @}
	}
	protected function solve($what) {
		//
		// Loading global pointers.
		global $wcDefaults;
		global $wcOptions;
		return !$wcDefaults["read-only"] && $wcOptions->activated($what);
	}
}
?>